<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>个人参考原文</title>
    <style type="text/css">
        body,
        div,
        h2,
        h3,
        ul,
        li,
        p {
            margin: 0;
            padding: 0;
        }

        input {
            outline: none;
        }

        a {
            text-decoration: none;
        }

        a:hover {
            text-decoration: underline;
        }

        ul {
            list-style-type: none;
        }

        body {
            color: #333;
            background: #3c3a3b;
            font: 12px/1.5 \5b8b\4f53;
        }

        #msgBox {
            width: 500px;
            background: #fff;
            border-radius: 5px;
            margin: 10px auto;
            padding-top: 10px;
        }

        #msgBox form {
            padding: 0 20px 15px;
            background: #0ff;
        }

        #msgBox form h2 {
            font-weight: 400;
            font: 400 18px/1.5 \5fae\bf6f\96c5\9ed1;
        }

        #userName,
        #conBox {
            color: #777;
            border: 1px solid #d0d0d0;
            border-radius: 5px;
            background: #fff;
        }

        #userName.active,
        #conBox.active {
            border: 1px solid #7abb2c;
        }

        #userName {
            height: 20px
        }

        #conBox {
            width: 448px;
            resize: none;
            height: 65px;

            overflow: auto;
        }

        #msgBox form div {
            position: relative;
            color: #999;
            margin-top: 10px;
        }

        #face {
            position: absolute;
            top: 0;
            left: 172px;
        }

        #face img {
            width: 30px;
            height: 30px;
            cursor: pointer;
            margin-right: 6px;
            opacity: 0.5;
        }

        #face img.hover,
        #face img.current {
            width: 28px;
            height: 28px;
            border: 1px solid #f60;
            opacity: 1;
        }

        #sendBtn {
            border: 0;
            width: 112px;
            height: 30px;
            cursor: pointer;
            margin-left: 10px;
            background: 0aa;
        }

        #sendBtn.hover {
            background: #0099FF;
        }

        #msgBox form .maxNum {
            font: 26px/30px Georgia, Tahoma, Arial;
            padding: 0 5px;
        }

        #msgBox .list {
            padding: 10px;
        }

        #msgBox .list h3 {
            position: relative;
            height: 33px;
            font-size: 14px;
            font-weight: 400;
            background: #e3eaec;
            border: 1px solid #dee4e7;
        }

        #msgBox .list h3 span {
            position: absolute;
            left: 6px;
            top: 6px;
            background: #fff;
            line-height: 28px;

            display: inline-block;
            padding: 0 15px;
        }

        #msgBox .list ul {
            overflow: hidden;
            zoom: 1;
        }

        #msgBox .list ul li {
            float: left;
            clear: both;
            width: 100%;
            border-bottom: 1px dashed #d8d8d8;
            padding: 10px 0;
            background: #fff;
            /* overflow: hidden; */
        }

        #msgBox .list ul li.hover {
            background: #f5f5f5;
        }

        #msgBox .list .userPic {
            float: left;
            width: 50px;
            height: 50px;
            display: inline;
            margin-left: 10px;
            border: 1px solid #ccc;
            border-radius: 3px;
        }

        #msgBox .list .content {
            float: left;
            width: 400px;
            font-size: 14px;
            margin-left: 10px;
            font-family: arial;
            word-wrap: break-word;
        }

        #msgBox .list .userName {
            display: inline;
            padding-right: 5px;
        }

        #msgBox .list .userName a {
            color: #2b4a78;
        }

        #msgBox .list .msgInfo {
            display: inline;
            word-wrap: break-word;
        }

        #msgBox .list .times {
            color: #889db6;
            font: 12px/18px arial;
            margin-top: 5px;
            overflow: hidden;
            zoom: 1;
        }

        #msgBox .list .times span {
            float: left;
        }

        #msgBox .list .times a {
            float: right;
            color: #889db6;
            display: none;
        }

        .tr {
            overflow: hidden;
            zoom: 1;
        }

        .tr p {
            float: right;
            line-height: 30px;
        }

        .tr * {
            float: left;
        }

    </style>
    <script type="text/javascript">
        /*-------------------------- +
             获取id, class, tagName
        +-------------------------- */
        var get = {
            byId: function(id) {
                return typeof id === "string" ? document.getElementById(id) : id
            },

            byClass: function(sClass, oParent) {
                // 存储需要的元素
                var aClass = [];
                // 设置字符串匹配模式:以正常字符开头或者空格开头的字符
                var reClass = new RegExp("(^| )" + sClass + "( |$)");
                // 列出该父元素下,所有标签
                var aElem = this.byTagName("*", oParent);
                // 匹配需要的元素
                for (var i = 0; i < aElem.length; i++) {
                    reClass.test(aElem[i].className) && aClass.push(aElem[i]);
                };
                return aClass;
            },

            byTagName: function(elem, obj) {
                return (obj || document).getElementsByTagName(elem);
            }
        }

        /*-------------------------- +
        	    事件绑定, 删除
        +-------------------------- */
        // 可以参考: https://blog.csdn.net/yangyixue123/article/details/79250786
        var EventUtil = {
            addHandler: function(oElement, sEvent, fnHandler) {
                oElement.addEventListener ?
                    oElement.addEventListener(sEvent, fnHandler, false) :
                    (
                        // 不理解这一步的意义？？？？？？？？？？？？？？？？？？？
                        oElement["_" + sEvent + fnHandler] = fnHandler,
                        oElement[sEvent + fnHandler] = function() {
                            // 执行函数的调用,fnHandler是执行函数的函数名,只有调用了才能用
                            oElement["_" + sEvent + fnHandler]()
                        },
                        oElement.attachEvent("on" + sEvent, oElement[sEvent + fnHandler])
                    )
            },
            removeHandler: function(oElement, sEvent, fnHandler) {
                oElement.removeEventListener ?
                    oElement.removeEventListener(sEvent, fnHandler, false) :
                    oElement.detachEvent("on" + sEvent, oElement[sEvent + fnHandler])
            },
            // window.onload的时间监听写法
            addLoadHandler: function(fnHandler) {
                this.addHandler(window, "load", fnHandler);
            }
        };
        /*-------------------------- +
                设置css样式
                读取css样式
        +-------------------------- */
        // obj,这是要操作对象,attr是css属性,value则是样式具体地点值
        function css(obj, attr, value) {
            switch (arguments.length) {
                case 2:
                    // ??????????????????这里为什么是object？？？？？？？？？？？？？？？？
                    // 参考下文得知：对象css的第二参数attr可能是json数据
                    if (typeof arguments[1] == "object") {
                        for (var i in attr) i == "opacity" ?
                            (obj.style["filter"] = "alpha(opacity)=" + attr[i] + ")", obj.style[i] = attr[i] / 100) :
                            obj.style[i] = attr[i];
                    } else {
                        // 前者兼容ie,后者兼容其他浏览器
                        return obj.currentStyle ? obj.currentStyle[attr] : getComputedStyle(obj, null)[attr]
                    }
                case 3:
                    attr == "opacity" ?
                        (obj.style["filter"] = "alpha(opacity)=" + value + ")",
                            obj.style[attr] = value / 100) :
                        obj.style[attr] = value;
                    break;
            }
        }

        EventUtil.addLoadHandler(function() {
            var oMsgBox = get.byId("msgBox");
            var oUserName = get.byId("userName");
            var oConBox = get.byId("conBox");
            var oSendBtn = get.byId("sendBtn");
            var oMaxNum = get.byClass("maxNum")[0];
            var oCountTxt = get.byClass("countTxt")[0];
            var oList = get.byClass("list")[0];
            var oUl = get.byTagName("ul", oList)[0];
            var aLi = get.byTagName("li", oList);
            var aFtxt = get.byClass("f-text", oMsgBox);
            var aImg = get.byTagName("img", get.byId("face"));

            var bSend = false;
            var timer = null;
            var oTmp = "";
            var i = 0;
            var maxNum = 140;

            // 禁止表单提交
            EventUtil.addHandler(get.byTagName("form", oMsgBox)[0], "submit", function() {
                return false;
            });
            // 为广播按钮绑定发送事件
            EventUtil.addHandler(oSendBtn, "click", fnSend);

            // 发送广播函数,同时检验输入字符的有效性并弹窗提示,如果没有错误,则正常广播
            function fnSend() {
                // 以\s或者多个\s开头并结束的字符,\s代表空白字符
                var reg = /^\s*$/g;
                if (reg.test(oUserName.value)) {
                    // 请填写您的姓名
                    alert("\u8bf7\u586b\u5199\u60a8\u7684\u59d3\u540d");
                    // 提示结束之后,会自动聚焦到需要输入内容的标签上
                    oUserName.focus();
                } else if (!/^[\u4e00-\u9fa5\w]{2,8}$/g.test(oUserName.value)) {
                    // \u4e00”和“\u9fa5”是unicode编码，并且正好是中文编码的开始和结束的两个值，
                    // 所以这个正则表达式可以用来判断字符串中是否包含中文
                    // 该句的意思,输入内容不符合格式为汉字/字母/数字/下划线且位数在2-8之间,则提示
                    // 姓名由2-8位字母、数字、下划线、汉字组成！
                    alert("\u59d3\u540d\u75312-8\u4f4d\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u4e0b\u5212\u7ebf\u3001\u6c49\u5b57\u7ec4\u6210\uff01");
                    oUserName.focus();
                } else if (reg.test(oConBox.value)) {
                    // 随便说点什么吧！
                    alert("\u968f\u4fbf\u8bf4\u70b9\u4ec0\u4e48\u5427\uff01");
                    oConBox.focus();
                } else if (!bSend) {
                    // 你输入的内容已超出限制，请检查！
                    alert("\u4f60\u8f93\u5165\u7684\u5185\u5bb9\u5df2\u8d85\u51fa\u9650\u5236\uff0c\u8bf7\u68c0\u67e5\uff01");
                    oConBox.focus();
                } else {
                    // 创建li标签
                    var oLi = document.createElement("li");
                    // 创建时间对象
                    var oDate = new Date();
                    // 每一行结尾带\,是为了代码中隐藏的换行符？？？？？？？？？？？？
                    //  /<[^>]*>|&nbsp;/ig    不太了解意思
                    oLi.innerHTML =
                        "<div class=\"userPic\"><img src=\"" + get.byClass("current", get.byId("face"))[0].src + "\" alt=\"\"></div>\
                            <div class=\"content\">\
                                <div class=\"userName\"><a href=\"javascript:void(0)\">" + oUserName.value + "</a></div>\
                                <div class=\"msgInfo\">" + oConBox.value.replace(/<[^>]*>|&nbsp;/ig, "") + "</div>\
                                <div class=\"times\"><span>" + format(oDate.getMonth() + 1) + "\u6708" + format(oDate.getDate()) + "\u65e5" + format(oDate.getHours()) + ":" + format(oDate.getMinutes()) + "</span><a href=\"javascript:void(0)\" class=\"del\">\u5220\u9664</a></div>\
                            </div>";
                    //插入元素到列表之前
                    aLi.length ? oUl.insertBefore(oLi, aLi[0]) : oUl.appendChild(oLi);
                    // 广播之后需要重置表单
                    get.byTagName("form", oMsgBox)[0].reset();
                    for (var i = 0; i < aImg.length; i++) aImg[i].className = "";
                    aImg[0].className = "current";
                    // 将元素高度保存
                    // 这里的css设置有什么意义
                    var iHeight = oLi.clientHeight - parseFloat(css(oLi, "paddingTop")) - parseFloat(css(oLi, "paddingBottom"));
                    var alpah = count = 0;
                    
                    // 这里使用的是缓动特效
                    //有异常，不能展现
//                    css(oLi, {
//                        "opacity": "0",
//                        "height": "0"
//                    });
//                    timer = setInterval(function() {
//                        css(oLi, {
//                            "display": "block",
//                            "opacity": "0",
//                            "height": (count += 0) + "px"
//                        });
//                        if(count > iHeight) {
//                            clearInterval(timer);
//                            css(oLi, "height", iHeight + "px");
//                            timer = setInterval(function() {
//                                css(oLi, "opacity", (alpah += 10));
//                                alpah > 100 && (clearInterval(timer),css(oLi, "opacity", 100));
//                            }, 30)
//                        }
//                    }, 30);
                    // 调用鼠标滑过、离开样式
                    liHover();
                    // 调用liHover之后，会同时出发删除按钮的显隐，此时可以使用删除功能
                    delLi();
                };
            };
            
            // 事件绑定，判断字符输入
            EventUtil.addHandler(oConBox, "keyup", confine);
            EventUtil.addHandler(oConBox, "focus", confine);
            EventUtil.addHandler(oConBox, "change", confine);
            
            // “广播”按钮鼠标滑过样式
            EventUtil.addHandler(oSendBtn, "mouseover", function() {
               this.className = "hover" 
            });
            // “广播”按钮鼠标离开样式
            EventUtil.addHandler(oSendBtn, "mouseout", function() {
               this.className = "" 
            });
            
            // 输入框获取焦点时样式
            for(let i=0; i<aFtxt.length; i++) {
                EventUtil.addHandler(aFtxt[i], "focus", function() {
                    this.className = "active"
                });
                EventUtil.addHandler(aFtxt[i], "blur", function() {
                    this.className = "";
                })
            }
            
            //头像选中的样式
            for(let i=0; i<aImg.length; i++) {
                EventUtil.addHandler(aImg[i], "mouseover", function() {
                    this.className += "hover"
                });
                // 图片类名有两个以上，故需要限制到底取消的是那个类名
                EventUtil.addHandler(aImg[i], "mouseout", function() {
                    this.className = this.className.replace(/\s?hover/, "")
                });
                EventUtil.addHandler(aImg[i], "click", function() {
                    for(let j=0; j<aImg.length; j++) aImg[j].className = "";
                    this.className = "current"
                });
            }
            
            /*---------------------------+
                    自定义功能函数
            +----------------------------*/
            // 格式化时间，如果为一位数时，补0
            function format(str) {
                return str.toString().replace(/^(\d)$/, "0$1");
            }
            // li标签鼠标滑过、离开处理函数
            function liHover() {
                for(let i=0;i<aLi.length;i++) {
                    // 鼠标滑过li的hover样式
                    EventUtil.addHandler(aLi[i], "mouseover", function(event) {
                        this.className = "hover";
                        oTmp = get.byClass("times", this)[0];
                        var aA = get.byTagName("a", oTmp);
                        if(!aA.length) {
                            var oA = document.createElement("a");
                            oA.innerHTML = "删除";
                            oA.className = "del";
                            oA.href = "javascript:;";
                            oTmp.appendChild(oA);
                        }else {
                            aA[0].style.display = "block"
                        }
                    });
                    
                    // 鼠标离开li的样式
                    EventUtil.removeHandler(aLi[i], "mouseout", function() {
                        this.className = "";
                        var oA = get.byTagName("a", get.byClass("times", this)[0])[0];
                        oA.style.display = "none";
                    })
                }
            }
            // 加载即调用
            liHover();
        
            // li标签删除功能
            function delLi() {
                var aA = get.byClass("del", oUl);
                
                for(let i=0;i<aA.length; i++) {
                    aA[i].index = i;
                    aA[i].onclick = function() {
                        var oParent = this.parentNode.parentNode.parentNode;
                        var alpha = 100;
                        var iHeight = oParent.offsetHeight;
                        timer = setInterval(function() {
                            css(oParent, "opacity", (alpha -= 10));
                            if(alpha < 0) {
                                clearInterval(timer);
                                timer = setInterval(function() {
                                    iHeight -= 10;
                                    iHeight < 0 && (iHeight = 0);
                                    
                                    css(oParent, "height", iHeight + "px");
                                    iHeight == 0 &&  (clearInterval(timer), oUl.removeChild(oParent))
                                }, 30)
                            }
                        }, 30)
                        this.onclick = null;
                    }
                }
            }
            // 建在即调用
            delLi();
            
            // 输入字符限制
            function confine() {
                var iLen = 0;
                // 检查变化的内容是否是汉字或者全角
                for(let i=0; i<oConBox.value.length; i++) iLen += /[^\x00-\xff]/g.test(oConBox.value.charAt(i)) ? 1 : 0.5;
                oMaxNum.innerHTML = Math.abs(maxNum - Math.floor(iLen));
                maxNum - Math.floor(iLen) >= 0 ? 
                    (oCountTxt.innerHTML = "\u8fd8\u80fd\u8f93\u5165", bSend = true) :
                    (css(oMaxNum, "color", "#f60"), oCountTxt.innerHTML = "\u5df2\u8d85\u51fa", bSend = false);
            }
            //加载即调用
            confine();
        });
    </script>
</head>

<body>
    <div id="msgBox">
        <form>
            <h2>来，说说你在做什么，想什么</h2>
            <div>
                <input type="text" id="userName" class="f-text" value="" />
                <p id="face">
                    <img src="image/1.gif" class="current" alt="">
                    <img src="image/2.gif" alt="">
                    <img src="image/3.gif" alt="">
                    <img src="image/4.gif" alt="">
                    <img src="image/5.gif" alt="">
                    <img src="image/6.gif" alt="">
                    <img src="image/7.gif" alt="">
                    <img src="image/8.gif" alt="">
                </p>
            </div>
            <div>
                <textarea id="conBox" class="f-text"></textarea>
            </div>
            <div class="tr">
                <p>
                    <span class="countTxt">还能输入</span><strong class="maxNum">140</strong><span>个字</span>
                    <input type="button" id="sendBtn" value="广播" title="快捷键 Ctrl+Enter">
                </p>
            </div>
        </form>

        <div class="list">
            <h3><span>大家在说</span></h3>
            <ul>
                <li>
                    <div class="userPic"><img src="image/1.gif" alt=""></div>
                    <div class="content">
                        <div class="userName"><a href="javascript:void(0)">日久生情</a></div>
                        <div class="msgInfo">新增删除广播功能</div>
                        <div class="times"><span>07月05日 15:14</span><a href="javascript:void(0)" class="del">删除</a></div>
                    </div>
                </li>
                <li>
                    <div class="userPic"><img src="image/1.gif" alt=""></div>
                    <div class="content">
                        <div class="userName"><a href="javascript:void(0)">日久生情</a></div>
                        <div class="msgInfo">新增Ctrl+Enter快捷键发送广播。</div>
                        <div class="times"><span>07月05日 12:20</span><a href="javascript:void(0)" class="del">删除</a></div>
                    </div>
                </li>
                <li>
                    <div class="userPic"><img src="image/1.gif" alt=""></div>
                    <div class="content">
                        <div class="userName"><a href="javascript:void(0)">日久生情</a></div>
                        <div class="msgInfo">新增选择头像功能。</div>
                        <div class="times"><span>07月05日 12:08</span><a href="javascript:void(0)" class="del">删除</a></div>
                    </div>
                </li>
                <li>
                    <div class="userPic"><img src="image/1.gif" alt=""></div>
                    <div class="content">
                        <div class="userName"><a href="javascript:void(0)">日久生情</a></div>
                        <div class="msgInfo">增加了记录广播时间的功能。</div>
                        <div class="times"><span>07月04日 16:55</span><a href="javascript:void(0)" class="del">删除</a></div>
                    </div>
                </li>
                <li>
                    <div class="userPic"><img src="image/1.gif" alt=""></div>
                    <div class="content">
                        <div class="userName"><a href="javascript:void(0)">日久生情</a></div>
                        <div class="msgInfo">增加了输入字符检测功能，英文/半角为半个字符，汉字/全角为一个字符。</div>
                        <div class="times"><span>07月04日 08:30</span><a href="javascript:void(0)" class="del">删除</a></div>
                    </div>
                </li>
                <li>
                    <div class="userPic"><img src="image/1.gif" alt=""></div>
                    <div class="content">
                        <div class="userName"><a href="javascript:void(0)">日久生情</a></div>
                        <div class="msgInfo">仿腾讯微博效果，欢迎大家测试！</div>
                        <div class="times"><span>07月03日 20:19</span><a href="javascript:void(0)" class="del">删除</a></div>
                    </div>
                </li>
            </ul>
        </div>
    </div>
</body>

</html>
